//	Parser.java

import java.io.*;
import java.util.StringTokenizer;

/** 
Description: A Parser for the 
<a href="http://cs.boisestate.edu/~tcole/cs354/fall08/grammar0.html">
Minimal Programming Language</a>.

<p><b>Parse tree structure</b>
 <div 
 align="center"
>
<table class="sam">
<tr><td><b>Token of root</b></td><td><b>left child</b></td>
<td><b>right child</b></td></tr>
<tr><td>ASSIGN</td><td>VAR containing name of variable to be
assigned</td><td>expression to be evaluated</td></tr>
<tr><td>PLUS, MINUS, TIMES, DIVIDE
</td><td>expression tree for left operand</td>
<td>expression tree for right operand</td></tr>
<tr><td>VAR<br>string value contains name</td><td>none</td><td>none</td></tr>
<tr><td>NUMBER<br>int value contains the number</td><td>none</td><td>none</td></tr>
</table>
</div>

@author Teresa Cole
@author Samuel Jenkins
@version CS354 Fall 2005 - modified Fall 2008
 */
public class Parser implements Token
{

    Lexer input;
    Lexeme next;
    /*Lexeme current, next;*/

    /** Construct a parser 
    
    @param in : the Lexer from which the input should come
     */
    public Parser(Lexer in)
    {
        input = in;
        next = new Lexeme(EOF);
    }

    /** Top-level parsing function for the calculator
    
    @return Lexeme which is root of parse tree for single expression
     */
    public Lexeme statement() throws SyntaxError
    {
        //System.out.println( "statement");
        advance();
        if (next == null || next.getType() == EOF)
        {
            return null;
        }
        Lexeme result = null;
        result = assignExpr();
        return result;
    }

    /** Parses an assignment expression
    
    @return parse tree for assignment expression
     */
    private Lexeme assignExpr() throws SyntaxError
    {
        //System.out.println( "assignExpr");
        Lexeme current, left, right;
        left = match(VAR);
        current = match(ASSIGN);
        right = expression();
        //System.out.println( "    right ");
        //right.printTree( System.out, 10);
        current.setLeft(left);
        current.setRight(right);
        if (!check(SEMIC))
        {
            throw new SyntaxError(SEMIC, next.getType());
        }
        return current;
    }

    /** Parses an expression
    
    @return parse tree for expression
     */
    private Lexeme expression() throws SyntaxError
    {
        //System.out.println( "--expression");
        Lexeme current, left;
        current = term();
        while (check(PLUS) || check(MINUS))
        {
            left = current;
            current = advance();
            current.setLeft(left);
            current.setRight(term());
        }
        return current;
    }

    /** Parses a term
    
    @return parse tree for term
     */
    private Lexeme term() throws SyntaxError
    {
        //System.out.println( "--term");
        Lexeme current, left;
        current = primary();
        //System.out.print( "---- first");
        //current.print( System.out);
        while (check(TIMES) || check(DIVIDE))
        {
            left = current;
            //System.out.print( "---- lhs");
            //left.print( System.out);
            current = advance();
            //System.out.print( "---- op");
            //current.print( System.out);
            current.setLeft(left);
            Lexeme right = primary();
            //System.out.print( "---- rhs");
            //right.print( System.out);
            current.setRight(right);
        }
        return current;
    }

    /** Parses a primary
    <pre>
    PRIMARY = NUMBER | (EXPRESSION)
    | VAR
    </pre>
    
    @return parse tree for primary
     */
    private Lexeme primary() throws SyntaxError
    {
        //System.out.print( "--primary: ");
        //next.print( System.out);
        Lexeme left, result, current;
        if (check(NUMBER))
        {
            result = advance();
        } else if (check(VAR))
        {
            //System.out.print( "    VAR case  ");
            result = advance();
        //result.print( System.out);
        } else
        {
            //System.out.print( "    parenthesized expression case  ");
            //next.print( System.out);
            current = match(OPAREN);
            result = expression();
            current = match(CPAREN);
        }
        return result;
    }

    /** gets the next token from the scanner and returns the current one
    
    @return current token
     */
    public Lexeme advance() throws SyntaxError
    {
        Lexeme current = next;
        next = input.next();
        return current;
    }

    /** gets the current token only if it has the correct type
    
    @param type is the Token required
    @return current Lexeme if type matches parameter
    @throws SyntaxError if current token does not have required type
     */
    private Lexeme match(int type) throws SyntaxError
    {
        Lexeme current = next;
        if (next.getType() == type)
        {
            next = input.next();
            return current;
        } else
        {
            System.out.println("current: " + current + "\nnext: " + next);
            throw new SyntaxError(type, current.getType());
        }
    }

    /** checks the current token to see if it has a particular type
    
    @param type is the Token to check for
    @return true if type matches parameter
     */
    private boolean check(int type)
    {
        return next.getType() == type;
    }
}
